﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

using Storm.TextEditor.Core;
using Storm.TextEditor.Win32;
using Storm.TextEditor.Win32.Enums;

namespace Storm.TextEditor.Drawing
{
    /// <summary>
    /// Represents a sealed class holding several methods for drawing.
    /// </summary>
    public sealed class DrawingTools
    {
        #region Methods

        #region Public

        /// <summary>
        /// Resets the given Graphics variable.
        /// </summary>
        /// <param name="g">Graphics to reset.</param>
        public static void ResetGraphics(Graphics g)
        {
            g.ReleaseHdc(g.GetHdc());
        }

        /// <summary>
        /// Mixes the given colors and returns the new color.
        /// </summary>
        /// <param name="c1">First color to mix.</param>
        /// <param name="c2">Second color to mix.</param>
        /// <param name="mix">Factor of the mixing.</param>
        /// <returns>The mixed color.</returns>
        public static Color MixColors(Color c1, Color c2, double mix)
        {
            return Color.FromArgb((int)(c1.R * (1 - mix) + c2.R * mix), (int)(c1.G *
                (1 - mix) + c2.G * mix), (int)(c1.B * (1 - mix) + c2.B * mix));
        }

        /// <summary>
        /// Draws a border depending on the given ControlBorderStyle with specific color at the given Rectangle.
        /// </summary>
        /// <param name="style">ControlBorderStyle that holds how the method should draw the border.</param>
        /// <param name="borderColor">Color of the drawn border.</param>
        /// <param name="g">Graphics to use when drawing.</param>
        /// <param name="r">Rectangle to draw the border at.</param>
        public static void DrawBorder(ControlBorderStyle style, Color borderColor, Graphics g, Rectangle r)
        {
            switch (style)
            {
                case ControlBorderStyle.Dotted:
                    {
                        r.Width--;
                        r.Height--;

                        g.DrawRectangle(new Pen(SystemColors.Control), r);
                        Pen p = new Pen(borderColor);
                        p.DashStyle = DashStyle.Dot;
                        g.DrawRectangle(p, r);

                        break;
                    }
                case ControlBorderStyle.Dashed:
                    {
                        r.Width--;
                        r.Height--;

                        g.DrawRectangle(new Pen(SystemColors.Control), r);
                        Pen p = new Pen(borderColor);
                        p.DashStyle = DashStyle.Dash;
                        g.DrawRectangle(p, r);

                        break;
                    }
                case ControlBorderStyle.Sunken:
                    {
                        if (borderColor == Color.Black)
                            borderColor = SystemColors.Control;

                        DrawingTools.DrawBorder(Border3DStyle.Sunken, borderColor, g, r);

                        break;
                    }
                case ControlBorderStyle.FixedSingle:
                    {
                        r.Width--;
                        r.Height--;

                        g.DrawRectangle(new Pen(borderColor), r);
                        break;
                    }
                case ControlBorderStyle.FixedDouble:
                    {
                        g.DrawRectangle(new Pen(borderColor), r.Left, r.Top, r.Width - 1, r.Height - 1);
                        g.DrawRectangle(new Pen(borderColor), r.Left + 1, r.Top + 1, r.Width - 3, r.Height - 3);

                        break;
                    }
                case ControlBorderStyle.Raised:
                    {
                        if (borderColor == Color.Black)
                            borderColor = SystemColors.Control;

                        DrawingTools.DrawBorder(Border3DStyle.Raised, borderColor, g, r);
                        break;
                    }
                case ControlBorderStyle.RaisedThin:
                    {
                        if (borderColor == Color.Black)
                        {
                            borderColor = Color.FromArgb(SystemColors.Control.R,
                                SystemColors.Control.G, SystemColors.Control.B);
                        }

                        DrawingTools.DrawBorder(Border3DStyle.RaisedInner,
                            borderColor, g, r);

                        break;
                    }
                case ControlBorderStyle.SunkenThin:
                    {
                        if (borderColor == Color.Black)
                        {
                            borderColor = Color.FromArgb(SystemColors.Control.R,
                                SystemColors.Control.G, SystemColors.Control.B);
                        }

                        DrawingTools.DrawBorder(Border3DStyle.SunkenOuter,
                            borderColor, g, r);
                        break;
                    }
                case ControlBorderStyle.Etched:
                    {
                        if (borderColor == Color.Black)
                            borderColor = SystemColors.Control;

                        ControlPaint.DrawBorder3D(g, r,
                            Border3DStyle.Etched);

                        break;
                    }
                case ControlBorderStyle.Bump:
                    {
                        if (borderColor == Color.Black)
                            borderColor = SystemColors.Control;

                        SolidBrush b = new SolidBrush(borderColor);
                        g.FillRectangle(b, r.Left, r.Top, r.Width, 4);
                        g.FillRectangle(b, r.Left, r.Bottom - 4, r.Width, 4);

                        g.FillRectangle(b, r.Left, r.Top, 4, r.Height);
                        g.FillRectangle(b, r.Right - 4, r.Top, 4, r.Height);
                        b.Dispose();

                        DrawingTools.DrawBorder(Border3DStyle.Raised, borderColor, g, r);
                        DrawingTools.DrawBorder(Border3DStyle.Sunken, borderColor, g,
                            new Rectangle(r.Left + 4, r.Top + 4, r.Width - 8,
                            r.Height - 8));

                        break;
                    }
                case ControlBorderStyle.Column:
                    {
                        SolidBrush normal = new SolidBrush(borderColor);
                        SolidBrush light = new SolidBrush(MixColors(borderColor,
                            Color.White, 1));

                        SolidBrush dark = new SolidBrush(MixColors(borderColor,
                            Color.Black, 0.4));

                        SolidBrush darkdark = new SolidBrush(Color.FromArgb
                            (borderColor.A, System.Windows.Forms.ControlPaint.
                            DarkDark(borderColor)));

                        g.FillRectangle(light, r.Left, r.Top, r.Width, 1);
                        g.FillRectangle(light, r.Left, r.Top + 3, 1, r.Height - 1 - 6);
                        g.FillRectangle(dark, r.Right - 1, r.Top + 3, 1, r.Height - 6);
                        g.FillRectangle(dark, r.Left, r.Bottom - 1, r.Width, 1);

                        break;
                    }
                case ControlBorderStyle.Row:
                    {
                        SolidBrush normal = new SolidBrush(borderColor);
                        SolidBrush light = new SolidBrush(MixColors(borderColor,
                            Color.White, 1));

                        SolidBrush dark = new SolidBrush(MixColors(borderColor,
                            Color.Black, 0.4));

                        SolidBrush darkdark = new SolidBrush(Color.FromArgb
                            (borderColor.A, System.Windows.Forms.ControlPaint.DarkDark
                            (borderColor)));

                        g.FillRectangle(light, r.Left + 3, r.Top, r.Width - 6, 1);
                        g.FillRectangle(light, r.Left, r.Top, 1, r.Height - 1);
                        g.FillRectangle(dark, r.Right - 1, r.Top, 1, r.Height);
                        g.FillRectangle(dark, r.Left + 3, r.Bottom - 1, r.Width - 6, 1);

                        break;
                    }
            }
        }

        /// <summary>
        /// Draws a line at the given coordinates.
        /// </summary>
        /// <param name="g">Graphics to use when drawing.</param>
        /// <param name="x1">Start x coordinate.</param>
        /// <param name="y1">Start y coordinate.</param>
        /// <param name="x2">End x coordinate.</param>
        /// <param name="y2">End y coordinate.</param>
        public static void DrawDesignTimeLine(Graphics g, int x1, int y1, int x2, int y2)
        {
            Pen p = new Pen(SystemColors.ControlDarkDark);

            p.DashOffset = 10;
            p.DashStyle = DashStyle.Dash;

            g.DrawLine(p, x1, y1, x2, y2);
            p.Dispose();
        }

        /// <summary>
        /// Draws the given image as a gray version.
        /// </summary>
        /// <param name="g">Graphics to use when drawing.</param>
        /// <param name="image">Image to draw.</param>
        /// <param name="x">X coordinate to draw the image at.</param>
        /// <param name="y">Y coordinate to draw the image at.</param>
        /// <param name="transparencyFactor">Factor of the transparency.</param>
        public static void DrawGrayImage(Graphics g, Image image, int x, int y, float transparencyFactor)
        {
            ColorMatrix cm = new ColorMatrix();
            ImageAttributes ia = new ImageAttributes();

            cm.Matrix33 = transparencyFactor;
            cm.Matrix00 = 0.33333334F;
            cm.Matrix01 = 0.33333334F;
            cm.Matrix02 = 0.33333334F;
            cm.Matrix10 = 0.33333334F;
            cm.Matrix11 = 0.33333334F;
            cm.Matrix12 = 0.33333334F;
            cm.Matrix20 = 0.33333334F;
            cm.Matrix21 = 0.33333334F;
            cm.Matrix22 = 0.33333334F;

            ia.SetColorMatrix(cm);
            g.DrawImage(image, new Rectangle(x, y, image.Width, image.Height), 0, 0,
                image.Width, image.Height, GraphicsUnit.Pixel, ia);
        }

        /// <summary>
        /// Draws the given image as a transparent version.
        /// </summary>
        /// <param name="g">Graphics to use when drawing.</param>
        /// <param name="image">Image to draw.</param>
        /// <param name="x">X coordinate to draw the image at.</param>
        /// <param name="y">Y coordinate to draw the image at.</param>
        /// <param name="transparencyFactor">Factor of the transparency.</param>
        public static void DrawTransparentImage(Graphics g, Image image, int x, int y, float transparencyFactor)
        {
            ImageAttributes ia = new ImageAttributes();
            ColorMatrix cm = new ColorMatrix();

            cm.Matrix33 = transparencyFactor;
            cm.Matrix00 = 1.0F;
            cm.Matrix11 = 1.0F;
            cm.Matrix22 = 1.0F;

            ia.SetColorMatrix(cm);
            g.DrawImage(image, new Rectangle(x, y, image.Width, image.Height), 0, 0,
                image.Width, image.Height, GraphicsUnit.Pixel, ia);
        }

        /// <summary>
        /// Draws a border using DashStyle.Dash at the given Rectangle.
        /// </summary>
        /// <param name="g">Graphics to use when drawing.</param>
        /// <param name="rect">Rectangle to draw the border at.</param>
        public static void DrawDesignTimeBorder(Graphics g, Rectangle rect)
        {
            rect.Width--;
            rect.Height--;

            Pen p = new Pen(SystemColors.ControlDarkDark);
            p.DashStyle = DashStyle.Dash;

            g.DrawRectangle(p, rect);
            p.Dispose();
        }

        public static void DrawInsertIndicatorH(int x, int y, int width, Graphics g, Color c)
        {
            y -= 3;
            x -= 2;

            ControlPaint.FillReversibleRectangle(new Rectangle(x, y, 2, 7), c);
            ControlPaint.FillReversibleRectangle(new Rectangle(x + 2, y + 1, width, 5), c);
            ControlPaint.FillReversibleRectangle(new Rectangle(width + 2 + x, y, 2, 7), c);
        }

        public static void DrawInsertIndicatorV(int x, int y, int height, Graphics g, Color c)
        {
            x -= 3;
            y -= 2;

            ControlPaint.FillReversibleRectangle(new Rectangle(x, y, 7, 2), c);
            ControlPaint.FillReversibleRectangle(new Rectangle(x + 1, y + 2, 5, height), c);
            ControlPaint.FillReversibleRectangle(new Rectangle(x, height + 2 + y, 7, 2), c);
        }

        /// <summary>
        /// Draws the given Control.
        /// </summary>
        /// <param name="control">Control to draw.</param>
        /// <returns>Bitmap created from the given Control.</returns>
        public static Bitmap DrawControl(Control control)
        {
            Bitmap b = new Bitmap(control.Width, control.Height);

            Graphics g = Graphics.FromImage(b);
            IntPtr hdc = g.GetHdc();

            int i = NativeUser32Api.SendMessage(control.Handle,
                (int)WindowMessage.WM_PRINT, (int)hdc,
                (int)(WMPrintFlags.PRF_CLIENT |
                WMPrintFlags.PRF_ERASEBKGND));

            g.ReleaseHdc(hdc);
            g.Dispose();

            return b;
        }

        /// <summary>
        /// Draws the given Control.
        /// </summary>
        /// <param name="control">Control to draw.</param>
        /// <param name="b">Bitmap to create graphics from.</param>
        /// <returns>Whether the drawing was succesful.</returns>
        public static bool DrawControl(Control control, Bitmap b)
        {
            Graphics g = Graphics.FromImage(b);
            IntPtr hdc = g.GetHdc();

            int result = NativeUser32Api.SendMessage(control.Handle, (int)WindowMessage.WM_PRINT, (int)hdc,
                (int)(WMPrintFlags.PRF_CLIENT | WMPrintFlags.PRF_ERASEBKGND));

            g.ReleaseHdc(hdc);
            g.Dispose();

            return result != 0;
        }

        public static void DrawSortArrow(int x, int y, Graphics g, bool ascending)
        {
            Color c1 = Color.FromArgb(220, 255, 255, 255);
            Color c2 = Color.FromArgb(140, 0, 0, 0);

            Pen p1 = new Pen(c1);
            Pen p2 = new Pen(c2);

            if (ascending == true)
            {
                g.DrawLine(p1, x, y + 6, x + 7, y + 6);
                g.DrawLine(p2, x + 3, y, x, y + 6);
                g.DrawLine(p1, x + 4, y, x + 7, y + 6);
            }
            else
            {
                g.DrawLine(p2, x, y, x + 7, y);
                g.DrawLine(p2, x, y, x + 3, y + 6);
                g.DrawLine(p1, x + 7, y, x + 4, y + 6);
            }
        }

        #endregion

        #region Private

        private static void DrawBorder(Border3DStyle Style, Color BorderColor, Graphics g, Rectangle r)
        {
            SolidBrush normal = new SolidBrush(BorderColor);
            SolidBrush light;

            if (BorderColor.GetBrightness() > 0.6)
                light = new SolidBrush(MixColors(BorderColor, Color.White, 1));
            else
                light = new SolidBrush(MixColors(BorderColor, Color.White, 0.5));

            SolidBrush dark;
            SolidBrush darkdark;

            if (BorderColor.GetBrightness() < 0.5)
            {
                dark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.7));
                darkdark = new SolidBrush(MixColors(BorderColor, Color.Black, 1));
            }
            else
            {
                dark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.4));
                darkdark = new SolidBrush(MixColors(BorderColor, Color.Black, 0.6));
            }

            switch (Style)
            {
                case Border3DStyle.Sunken:
                    {
                        g.FillRectangle(dark, r.Left, r.Top, r.Width, 1);
                        g.FillRectangle(dark, r.Left, r.Top, 1, r.Height);
                        g.FillRectangle(darkdark, r.Left + 1, r.Top + 1, r.Width - 2, 1);
                        g.FillRectangle(darkdark, r.Left + 1, r.Top + 1, 1, r.Height - 2);

                        g.FillRectangle(light, r.Right - 1, r.Top + 1, 1, r.Height - 1);
                        g.FillRectangle(light, r.Left + 1, r.Bottom - 1, r.Width - 1, 1);
                        g.FillRectangle(normal, r.Right - 2, r.Top + 2, 1, r.Height - 3);
                        g.FillRectangle(normal, r.Left + 2, r.Bottom - 2, r.Width - 3, 1);
                        break;
                    }
                case Border3DStyle.Raised:
                    {
                        g.FillRectangle(normal, r.Left, r.Top, r.Width - 1, 1);
                        g.FillRectangle(normal, r.Left, r.Top, 1, r.Height - 1);
                        g.FillRectangle(light, r.Left + 1, r.Top + 1, r.Width - 2, 1);
                        g.FillRectangle(light, r.Left + 1, r.Top + 1, 1, r.Height - 2);

                        g.FillRectangle(darkdark, r.Right - 1, r.Top, 1, r.Height);
                        g.FillRectangle(darkdark, r.Left, r.Bottom - 1, r.Width, 1);
                        g.FillRectangle(dark, r.Right - 2, r.Top + 1, 1, r.Height - 2);
                        g.FillRectangle(dark, r.Left + 1, r.Bottom - 2, r.Width - 2, 1);

                        break;
                    }
                case Border3DStyle.RaisedInner:
                    {
                        g.FillRectangle(light, r.Left, r.Top, r.Width - 1, 1);
                        g.FillRectangle(light, r.Left, r.Top, 1, r.Height - 1);

                        g.FillRectangle(dark, r.Right - 1, r.Top, 1, r.Height);
                        g.FillRectangle(dark, r.Left, r.Bottom - 1, r.Width, 1);

                        break;
                    }
                case Border3DStyle.SunkenOuter:
                    {
                        g.FillRectangle(dark, r.Left, r.Top, r.Width, 1);
                        g.FillRectangle(dark, r.Left, r.Top, 1, r.Height);

                        g.FillRectangle(light, r.Right - 1, r.Top + 1, 1, r.Height - 1);
                        g.FillRectangle(light, r.Left + 1, r.Bottom - 1, r.Width - 1, 1);

                        break;
                    }
                case Border3DStyle.Etched:
                    break;
                default:
                    break;
            }

            normal.Dispose();
            light.Dispose();
            dark.Dispose();
            darkdark.Dispose();
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of DrawingTools.
        /// </summary>
        private DrawingTools()
        {
        }
    }
}
